#include <xpp/net/TcpSocket.h>
#include <xpp/net/Tcp4Address.h>
#include <xpp/net/Tcp6Address.h>
#include <xp/bas/string.h>

using namespace xpp;
using namespace xpp::net;

void print_address (const xp_char_t* title, const InetAddress& addr)
{
	xp_char_t hbuf0[100], sbuf0[100];
	xp_char_t hbuf1[NI_MAXHOST + 1], sbuf1[NI_MAXSERV + 1];

	if (addr.getNumericHost (hbuf0, xp_countof(hbuf0)) == -1)
		xp_strxcpy (hbuf0, xp_countof(hbuf0), XP_TEXT("*unknown*"));
	if (addr.getNumericService (sbuf0, xp_countof(sbuf0)) == -1)
		xp_strxcpy (sbuf0, xp_countof(sbuf0), XP_TEXT("*unknown*"));
	if (addr.getHostName (hbuf1, xp_countof(hbuf1)) == -1)
		xp_strxcpy (hbuf1, xp_countof(hbuf1), XP_TEXT("*unknown*"));
	if (addr.getServiceName (sbuf1, xp_countof(sbuf1)) == -1)
		xp_strxcpy (sbuf1, xp_countof(sbuf1), XP_TEXT("*unknown*"));

	xp_printf (XP_TEXT("%s\n"), title);
	xp_printf (
		XP_TEXT("\tisLoopBack: %d isAnyLocal: %d\n"),
		addr.isLoopBack(), addr.isAnyLocal());
	xp_printf (
		XP_TEXT("\tNumeric: %s,%s Name: %s,%s\n"), hbuf0, sbuf0, hbuf1, sbuf1);
}

extern "C" { int xp_main (); }

int xp_main ()
{
	if (Socket::startup() == -1) {
		xp_printf (XP_TEXT("can not initialize the socket subsystem\n"));
		return -1;
	}

	try {
		/*
		Tcp6Address me (XP_TEXT("2000"));
		Tcp6Address peer;
		*/
		Tcp6Address me (XP_TEXT("2000"));

		TcpSocket socket;

		if (socket.open (me) == -1) {
			xp_printf (XP_TEXT("open failure: %d\n"), socket.errorCode());
			Socket::cleanup ();
			return -1;
		}

		// TODO: how can i set the options????
		printf ("setIpv6Only = %d\n", socket.setIpv6Only (1));
		printf ("setReuseAddr = %d\n", socket.setReuseAddr (1));

		if (socket.bind () == -1) {
			xp_printf (XP_TEXT("bind failure: %d\n"), socket.errorCode());
			Socket::cleanup ();
			return -1;
		}

		if (socket.listen (5) == -1) {
			xp_printf (XP_TEXT("listen failure: %d\n"), socket.errorCode());
			Socket::cleanup ();
			return -1;
		}

		for (;;) {
			xp_byte_t buf[1000];
			int n;
			
			TcpSocket peer_socket;
			Tcp6Address peer_addr;
			if (socket.accept (&peer_socket, &peer_addr) == -1) {
				xp_printf (XP_TEXT("accept failure: %d\n"), socket.errorCode());
				Socket::cleanup ();
				return -1;
			}
			print_address (XP_TEXT("peer"), peer_addr);

			n = peer_socket.receive (buf, xp_sizeof(buf));
			if (n == 0) break;
			if (n == -1) {
				xp_printf (XP_TEXT("receive failure: %d\n"), socket.errorCode());
				Socket::cleanup ();
				return -1;
			}

			n = peer_socket.send (buf, n);
			if (n == -1) {
				xp_printf (XP_TEXT("receive failure: %d\n"), socket.errorCode());
				Socket::cleanup ();
				return -1;
			}


			peer_socket.close ();
		}

		socket.close ();
	}
	catch (Exception& ex) {
		xp_printf (XP_TEXT("exception: %s, %s\n"), 
			XPP_EXCEPTION_NAME(ex), XPP_EXCEPTION_MSG(ex));
	}

	Socket::cleanup ();
	return 0;
}
